var $ = window.$;
var event = require('../../core/event');
var vector = require('./vector');
var timer = require('../../core/timer');
var status = require('../toggle/ui');
var sample = require('../sample/manager');
var layer = require('../layer/manager');
var notify = require('../notify/ui');
var frac = require('../../util/frac');
var Log = require('../../util/logger');
var util = require('../../util/index');
var input = require('../../core/input');
var jade = require('jade');
var sound = require('../audio/sound');
var gridUtil = require('./util');
var config = require('../../core/config');
var timing = require('../timing/manager');
var record = require('../../core/record');
var enums = require('../../core/enum');

/**
 * 1. 播放时-> update控制刷新
 * 2. seek时-> timeline组件计算时间，通知manager
 * 3. 点击定位-> mouseup事件模拟click
 * 4. 拖动-> mousemove事件
 * 5. 大范围选择->mousedown
 * 6. 缩放比例->重算布局,重定位
 */

/**
 *  -----------  <--note area上边, 总高为 H
 *
 *  **    **
 *
 *    **  **
 *
 *  **  **  **
 *  -----------  <--可见区上边, 总高为 h
 *      **
 *  **
 *    **
 *  -----------  <--可见区下边
 *
 *  **  **
 *
 *  -----------  <--note area下边, 靠translate3d(0,y,0) 移动,y值为正
 *
 *  边缘情况是 上边与上边对齐,或下边与下边对齐.
 *  H和h 初始是一样高
 *  实际H总要比最后一个note的y值再多h 高度
 * */

var GridUI = function() {

};

GridUI.prototype = {
	init: function(){
		if(this._inited){
			return;
		}
		var self = this;

		this._beatH = 100;
		this._noteH = 20;  // 跟css里关联
		this._columnW = 64;
		this._columnCount = 15;
		this._gridPadding = 75;
		this._gridBtY = 100;   //栅格0点离底部的px距离
        this.trackSelectMode = {};//记录本次选择整轨是正选还是反选

		this.parent = $('#edit_up');
		var tmpl = jade.compileFile('./tmpl/grid/key.jade');
		this.parent.html(tmpl());

		this.editArea = $('#edit_area');
		this.grid = $('#grid');
		this.note = $('#note_area');
		this.line = $('#grid_line');
		this.currline = null;
		this.select = $('#select_area');
		this.tip = $('#note_tip');

		var plugin = require('./plugin/edit');
		this.pluginEdit = new plugin();
		this.pluginEdit.setUI(this);
		this.pluginCopy = require('./plugin/copy');
		this.pluginCopy.setUI(this);
        this.pluginUndo = require('./plugin/undo');
        this.pluginUndo.init();
        this.pluginUndo.setUI(this);
		plugin = require("./plugin/meta");
		this.pluginMeta = new plugin();
		this.pluginMeta.init();
		this.pluginMeta.setUI(this);
        this.record = record;
		this.focus = false;
		this.hover = false;
		this.playNoteId = -1;
		this.playId = -1;

		var h = this.parent.height();
		var w = this.parent.width();

		this.bind();

		this.isMouseDown = false;
		this.noteAreaY = 0;  //note area 下边的偏移值
		this.gridScale = 1.0;  //1 beat的缩放值
		this.gridScaleH = this._beatH * this.gridScale;  //这两个要联动
		this.gridH = this.grid.height();
		this.divide = 4;
		this.lastUpdate = 0;
		this.needUpdate = false;
		this.selectScroll = 0;   //鼠标按下状态, 且位于grid上下边缘时, 有个时间seek附属效果
		this.fastSeekTime = NaN;
		this.seekTimer = null;
		timing.init();
        record.init();
		this.updateGrid();
		this._inited = true;
	},

	bind: function() {
		this.editArea.on('mousedown', this.onMouseEvent.bind(this));
		this.editArea.on('mousemove', this.onMouseEvent.bind(this));
		this.editArea.on('mouseup', this.onMouseEvent.bind(this));
		this.parent.on('mousewheel', this.onParentEvent.bind(this));
		this.parent.on('mousedown', this.onParentEvent.bind(this));
		this.parent.on('mousemove', this.onParentEvent.bind(this));
		this.parent.on('mouseup', this.onParentEvent.bind(this));
		this.line.on('click', 'span', function(e) {
			var beat = $(this).data('beat').split(',');
			beat = util.parseIntArray(beat);
			event.trigger(event.events.dialog.bpm, beat);
		});
		this.timerId = timer.registUpdate(this.onUpdate.bind(this), 60);
		event.bind(event.events.key.up, 'grid_ui', this);
		event.bind(event.events.key.repeat, 'grid_ui', this);
		event.bindOwner('global', 'grid_ui', this);
		event.bindOwner('grid', 'grid_ui', this);
	},

	/**
	 * 对外接口
	 */

	setManager: function(ma){
		this.manager = ma;
	},

	show: function(){
		this.parent.css('visibility','visible');
	},

	hide: function(){
		this.parent.css('visibility','hidden');
	},

	release: function() {
		timer.unregisterUpdate(this.timerId);
		event.unbindByKey('grid_ui');
		event.unbindOwner('global', 'grid_ui');
		event.unbindOwner('grid', 'grid_ui');

		this.parent.off('mousewheel');
		this.parent.off('mousedown');
		this.parent.off('mousemove');
		this.parent.off('mouseup');
		this.parent = null;
		this.manager = null;
		this.pluginCopy = null;
		this.pluginEdit = null;
		this.pluginSelect = null;
		this._inited = false;
		if(this.pluginUndo) {
			this.pluginUndo.release();
			this.pluginUndo = null;
		}
		this.pluginMeta.release();
		this.pluginMeta = null;
		$("body").removeAttr("class");
	},

	clear: function(){
		this.note.html('');
		this.line.html('');
	},

	/**
	 * 强制同步全部note
	 * 目前只有第一次加载note需要调用
	 */
	sync: function(){
		this.note.html('');
		var _grid = this.grid.find('div');
		_grid.attr('class','');

		var notes = this.manager.getNotes();
		if(!notes){
			return;
		}
		var info = this.manager.getEditInfo();
		this.divide = gridUtil.clampDivide(config.getVal(config.ITEM.GRID_DIVI) || 4);
		this.gridScale = config.getVal(config.ITEM.GRID_ZOOM) || 1;
		this.gridScaleH = this.gridScale * this._beatH;

		//重置可编辑区域长度
		var len = 0;
		if(info.grid && info.grid.length){
			len = info.grid.length;
		}else{
			len = this.manager.getMaxNoteTime() + 3000;
		}
		sound.setMaxLength(Math.max(len, 60000));  //最少60s


		var htmls = [];
		for(var i=0;i<notes.length;i++){
			var note = notes[i];

			var html = this.makeNoteDiv(note);
			htmls.push(html);
		}
		this.note.append(htmls.join(''));

		//debugger;
		var exist = info.toggle;
		var colCount = 0;
		for(var i=0;i<exist.length;i++){
			if(exist[i] == 1){
				_grid.eq(i).addClass('note');
				colCount++;
			}else if(exist[i] == 2){
				_grid.eq(i).addClass('sound');
				colCount++;
			}
		}

		this._columnCount = colCount;

		this.updateGrid();
		this.needUpdate = true;
		this.onUpdate();
		//完全同步时, 自动获得焦点
		if(!this.focus){
			this.focus = true;
			event.trigger(event.events.global.blur, null, this);
		}

	},


	/**
	 * 事件响应
	 */

	onMouseEvent: function(e){
		//这里做点标记工作,具体逻辑交给分函数

		switch(e.type){
			case 'mousedown':
				this.isMouseDown = true;
				if(!this.focus){
					this.focus = true;
					event.trigger(event.events.global.blur, null, this);
				}

				this.onMouseDown(e);
				e.stopPropagation();
				break;
			case 'mousemove':
				if(!this.isMouseDown){
					return;
				}
				this.onMouseMove(e);
				break;
			case 'mouseup':
				if(!this.isMouseDown){
					return;
				}
				this.isMouseDown = false;
				this.onMouseUp(e);
				break;

		}
	},

	onParentEvent: function (e) {
		//我们监听了parent, 而parent有一些元素不应该响应事件, 在这里排除
		if($(e.target).parents('#edit_tool').size()){
			return;
		}
		switch (e.type){
			case 'mousewheel':
				var delta = e.originalEvent.wheelDelta / 120;
				this.onScroll(e, delta, true);
				break;
			case 'mousedown':
				this.isParentDown = true;
				if(!this.focus){
					this.focus = true;
					event.trigger(event.events.global.blur, null, this);
				}
				//this.cancelSelect();
				this.onMouseDown(e);
				break;
			case 'mousemove':
				if(this.isParentDown || this.isMouseDown){
					this.onMouseMove(e);
				}
				break;
			case 'mouseup':
				if(this.isParentDown || this.isMouseDown){
					this.isParentDown = false;
					this.onMouseUp(e);
				}
				break;

		}
	},

	cancelMouse: function(){
		this.isMouseDown = false;
		this.isParentDown = false;
	},

	onMouseDown: function(e){
		this.mouseStart = vector.sub([e.pageX, e.pageY], this.offset);
		var triggerKey = e.which; //1 左键, 3右键

		var pos = this.mouseToLayer(this.mouseStart);
		var gridPos = this.layerToGridOffset(pos);
		var virtualNote = this.paramToNote(gridPos);  //当前位置理论上的note
		var note;
		//存在-100px的偏移, 不要处理负数范围的点击
		if(gridPos[0] < 0){
			this.cancelMouse();
			return;
		}
		//识别note 优先走tag id的方式
		if(e.target.tagName == 'DIV'){
			var _id = +$(e.target).data('tag');
			if(_id >= 0){
				note = this.manager.getNote(_id);
			}else{
				note = this.manager.findAnyNote(virtualNote);
			}
		}else{
			note = this.manager.findAnyNote(virtualNote);  //位置上实际存在的note
		}

		//删除
		if(note && triggerKey == 3){
			//普通note删除逻辑
            this.deleteNote(note);
			event.trigger(event.events.note.change);
			this.cancelMouse();
			return;
		}else if(triggerKey == 3){
			this.cancelSelect();
			this.cancelMouse();
			return;
		}

		this.pluginEdit.onMouseDown(this.mouseStart, note, e);
	},

    deleteNote : function(note){
        this.pluginUndo.mark("before_del", note.id);
        this.manager.removeNote(note.id);
        var div = this.getNoteDiv(note.id);
        div.remove();
        if(note.sound && note.tid >= 0){
            sound.removeCell(note.tid);
            note.tid = -1;
        }
        this.pluginMeta.update();
    },

	onMouseMove: function(e){
		var pos = vector.sub([e.pageX, e.pageY], this.offset);

		this.pluginEdit.onMouseMove(pos, e);

		//是否贴边, 进行fast seek
		if(pos[1] < 40){
			this.selectScroll = (40 - pos[1]);
		}else if(pos[1] > this.gridH - 40){
			this.selectScroll = -(40 - this.gridH + pos[1]);
		}else{
			this.selectScroll = 0;
		}
	},

	onMouseUp: function(e){
		var pos = vector.sub([e.pageX, e.pageY], this.offset);

		this.pluginEdit.onMouseUp(pos, e);

		this.selectScroll = 0;
	},

	onScroll: function(e, delta, wheel){
        var isCtrl = e ? e.ctrlKey : false;
        var isShift = e ? e.shiftKey : false;
        var isAlt = e ? e.altKey : false;
		if(isCtrl && wheel) {
			//改变divide
			if(delta > 0) {
				if(this.divide < 16) {
					this.divide++;
					this.divide = gridUtil.clampDivide(this.divide, true);
				} else
					return;
			} else if(delta < 0) {
				if(this.divide > 2) {
					this.divide--;
					this.divide = gridUtil.clampDivide(this.divide);
				} else
					return;
			}
			this.updateGrid();
			var info = this.manager.getEditInfo();
			if(!info.grid){
				info.grid = {};
			}
			config.update(config.ITEM.GRID_DIVI, this.divide);
			event.trigger(event.events.note.change);
		}else if(isShift && wheel){
			//改scale
			this.gridScale += delta > 0? 0.1: -0.1;
			this.gridScale = util.clamp(this.gridScale, 1, 3);
			this.gridScaleH = this.gridScale * this._beatH;
			this.updateGrid();
			this.updateNotes();
			var info = this.manager.getEditInfo();
			if(!info.grid){
				info.grid = {};
			}
			config.update(config.ITEM.GRID_ZOOM, this.gridScale);

			event.trigger(event.events.note.change);
		}else{
			//滚动状态是在audio方向匀速
            if(wheel) {
                var now = sound.stableTime();
                var nowBeat = timing.timeToBeat(now, this.divide);
                var nextBeat = frac.add(nowBeat, isAlt ? [1 * delta, 0, 0] : [0, 1 * delta, this.divide]);
                sound.fastSeek(Math.max(0, timing.beatToTime(nextBeat)));
            }else{
                sound.fastSeek(Math.max(0, this.lastUpdate + delta * 100));
            }
		}

	},

    /**
     * 选择整个音轨上的note
     * @param idx 轨道id
     */
    trackSelect : function(idx){
        var notes = this.manager.getNotes();
        if(!notes){
            return;
        }
        var selectMode = this.trackSelectMode[idx] || 1;//第一次做全选
        for(var i = 0 ; i < notes.length ; i++){
            var note = notes[i];
            if(note.column == idx){
				var div = this.getNoteDiv(note.id);
				if(selectMode == 1){
					div.addClass("curr");
				}else{
					div.removeClass("curr");
				}
            }
        }
        this.trackSelectMode[idx] = -selectMode;
    },

	onEvent: function(e, param){
		if(e == event.events.key.up) {
			if (!this.focus) {
				return;
			}
			switch (param.key) {
				case 27: //esc
					this.cancelSelect();
					this.stopPlay();
					return event.result.stopAll;
				case 37: //<--
					break;
				case 38: // 上
					this.onScroll(e, 1, true);
					break;
				case 39:// -->
					break;
				case 40://下
					this.onScroll(e, -1, true);
					break;
				case 8: //backspace
				case 46://del
					this.deleteSelect();
					return event.result.stopAll;
				case enums.KEY.A:
					this.selectAll();
					return event.result.stopAll;
			}
		}else if(e == event.events.key.repeat){
			if (!this.focus) {
				return;
			}
			switch (param.key) {
				case 38: // 上
					this.onScroll(e, 1, true);
					break;
				case 40://下
					this.onScroll(e, -1, true);
					break;
			}
		}else if(e == event.events.global.blur) {
			this.focus = false;
			//this.cancelSelect();
		}else if(e == event.events.global.copy) {
			if (!this.focus) {
				return;
			}
			this.pluginCopy.onCopy();
		}else if(e == event.events.global.paste){
			if (!this.focus) {
				return;
			}
			this.pluginCopy.onPaste();
		}else if(e == event.events.grid.change){
			this.updateGridConfig(param);
		}else if(e == event.events.grid.layer){
			//note的颜色发生变化, 刷新
			this.updateNoteColor(param);
		}else if(e == event.events.grid.focus){
			if(!param){
				return;
			}
			this.highlight(param);
		}else if(e == event.events.grid.line){
			//根据param指示高亮一行
			this.showLine(param);
		}else if(e == event.events.global.flipH){
            this.flipNotes();
        }else if(e == event.events.global.mirrorH){
            this.mirrorFlip();
        }else if(e == event.events.grid.bpm){
			this.updateBPM();
		}else if(e == event.events.grid.align){
			this.updateNotesPos(param);
		}

	},

	/**
	 * 内部方法
	 */

	/**
	 * 检查是否超过了grid可表示的时间上限
	 * 实际是sound的max值
	 * @param time
	 */
	checkMaxTime: function(time){
		var _max = sound.getMaxLength();
		if(time > _max - 20000){
			_max += 30000;
			sound.setMaxLength(_max); //+30s
			var info = this.manager.getEditInfo();
			util.setJsonChainValue(info, 'grid.length', _max);
			this.updateGrid();
			//todo 通知timeline也更新, 但是grid不应该直接和timeline有关联
			event.trigger(event.events.note.change);
		}
	},

	updateGridConfig: function() {
		//仅当scale变化时才需要重置note
		//其他情况只改节拍线
		var _divide = config.getVal(config.ITEM.GRID_DIVI) || 4;
		var _zoom = config.getVal(config.ITEM.GRID_ZOOM) || 1;
		var changed = false;
		if(_zoom != this.gridScale){
			this.gridScale = _zoom;
			this.gridScaleH = this.gridScale * this._beatH;
			this.updateNotes();
			changed = true;
		}
		if(_divide != this.divide){
			this.divide = _divide;
			changed = true;
		}
		if(changed){
			this.updateGrid();
		}

		//检查轨道
		var exist = util.getJsonChainValue(this.manager.chart, "extra.toggle");
		var _grid = this.grid.find('div');
		var colCount = 0;
		for(var i=0;i<exist.length;i++){
			if(exist[i] == 1){
				_grid.eq(i).addClass('note');
				colCount++;
			}else if(exist[i] == 2){
				_grid.eq(i).addClass('sound');
				colCount++;
			}else{
				_grid.eq(i).attr("class", "");
			}
		}
		this._columnCount = colCount;

		var rect = this.grid[0].getBoundingClientRect();
		this.offset = [rect.left + this._gridPadding, rect.top];
	},

	/**
	 * 当缩放比发生变化时, 重置界面尺寸
	 * 重置节拍线
	 */
	updateGrid: function(){
		var rect = this.grid[0].getBoundingClientRect(); //不能用note area节点来参考, 他会被translate导致clientrect也变动
		this.offset = [rect.left + this._gridPadding, rect.top];
		//根据音频长度,填满栅格
		var len = sound.getMaxLength();
		var beatCount = Math.ceil(timing.timeToFloatBeat(len)) + 3;

		//节拍线
		this.line.html('');
		var htmls = [];
		for(var i=0;i<beatCount;i++){
			var startY = i * this.gridScaleH;
			var html = '<i class="hr" style="bottom:'+(i*this.gridScaleH)+'px;"></i>';
			htmls.push(html);
			html = '<em style="bottom:'+(i*this.gridScaleH)+'px;">'+(i+1)+'</em>';
			htmls.push(html);
			for(var j=1;j<this.divide;j++){
				//染色
				var red = frac.gcd(j, this.divide);
				var _red = this.divide / red;
				var cls = '';
				if(_red == 2){
					cls="h2";
				}else if(_red == 3){
					cls="h3";
				}else if(_red == 4){
					cls="h4";
				}else{

				}
				html = '<i class="'+cls+'" style="bottom:'+(startY + j*(this.gridScaleH / this.divide))+'px;"></i>';
				htmls.push(html);
			}
		}

		this.line.append(htmls.join(''));

		this.updateBPM();
	},

	updateBPM: function() {
		this.line.find('span').remove();
		//bpm标记
		var bpms = timing.getAll();
		var htmls = [];
		if(bpms && bpms.length){
			for(var i=0;i<bpms.length;i++){
				var bpm = bpms[i];
				var y = this.gridToLayerY(bpm.beat);
				var html = '<span data-beat="'+bpm.beat.join(',')+'" style="bottom:'+y+'px;">'+Math.round(bpm.bpm)+'</span>';
				htmls.push(html);
			}
			this.line.append(htmls.join(''));
		}
	},

	/**
	 * grid change后, 重新摆放note的位置
	 */
	updateNotes: function(param){
		var notes;
		if(param && param.selected){
			notes = this.note.find('.curr');
		}else{
			notes = this.note.find('div');
		}
		for(var i=notes.size()-1;i>=0;i--){
			var id = +notes.eq(i).data('tag');
			var note = this.manager.getNote(id);
			this.alignNote(note, notes.eq(i));
		}
	},

	/**
	 * note beat发生了变化, 重新定位note的图形和音频位置
	 * @param param
	 */
	updateNotesPos: function(param){
		var notes;
		if(param && param.selected){
			notes = this.note.find('.curr');
		}else{
			notes = this.note.find('div');
		}
		for(var i=notes.size()-1;i>=0;i--){
			var id = +notes.eq(i).data('tag');
			var note = this.manager.getNote(id);
			this.alignNote(note, notes.eq(i));
			if(note.tid >= 0){
				var time = timing.beatToTime(note.beat) + (note.offset || 0);
				sound.offsetCell(note.tid, time);
				this.checkMaxTime(time);
			}
		}
	},

	/**
	 * 重置note的颜色, 不改变选中属性
	 * @param isAll
	 */
	updateNoteColor: function(isAll) {
		var notes;
		if(isAll){
			notes = this.note.find('div');
		}else{
			notes = this.note.find('.curr');
		}
		for(var i=notes.size()-1;i>=0;i--){
			var id = +notes.eq(i).data('tag');
			var note = this.manager.getNote(id);
			var color = layer.getColor(note.color);
			if(color){
				notes.eq(i).css('backgroundColor', color);
			}else{
				notes.eq(i).css('backgroundColor', '');

			}
		}
	},

	updateTip: function(note){
		var html = util.formatStr("Time:{0}:{1}/{2}", [note.beat[0]+1, note.beat[1], note.beat[2]]);
		if(note.endbeat){
			html += util.formatStr(" - {0}:{1}/{2}", [note.endbeat[0]+1, note.endbeat[1], note.endbeat[2]]);
		}
		var lines = this.tip.find('span');
		lines.eq(0).html(html);
		if(note.sound){
			html = util.formatStr("Sound:{0}", [note.sound]);
			lines.eq(1).html(html).show();
		}else{
			lines.eq(1).hide();
		}
		if(note.vol){
			html = util.formatStr("Volume:{0}", [note.vol]);
			lines.eq(2).html(html).show();
		}else{
			lines.eq(2).hide();
		}
	},

	highlight: function(param){
		this.cancelSelect();
		var notes = this.note.find('div');
		var len = notes.size();
		var minTime = [999999999, 0, 1];
		if(typeof param == 'object' && param.length){
			//数组模式, 指定一批id
			for(var i=0;i<param.length;i++){
				var note = this.note.find('div[data-tag="'+param[i]+'"]');
				note.addClass('curr');
				var _note = this.manager.getNote(param[i]);
				if(frac.compare(_note.beat, minTime) < 0){
					minTime = _note.beat;
				}
			}
		}else{
			//属性模式, 选中相同属性
			for(var i=0;i<len;i++){
				var note = notes.eq(i);
				var id = +note.data('tag');
				var _note = this.manager.getNote(id);
				if(!_note){
					continue;
				}
				if(param.id && _note.id != param.id){
					continue;
				}
				if(param.sound && _note.sound != param.sound){
					continue;
				}
				if(param.color >= 0 && _note.color != param.color){
					continue;
				}
				note.addClass('curr');
				if(frac.compare(_note.beat, minTime) < 0){
					minTime = _note.beat;
				}
			}
		}
		//定位到这批note最小time的那个
        if(minTime[0] == 999999999){
            return;
        }
		if(!param.noseek){
			var time = timing.beatToTime(minTime);
			sound.seekTo(time);
		}

	},

	onUpdate: function(){
		if(Math.abs(this.selectScroll) > 1e-6){
			//处于拖动卷动状态
			this.onScroll(null, this.selectScroll / 150);
		}

		var time = sound.stableTime();
		//var len = sound.getMaxLength();
		if(!this.needUpdate && this.lastUpdate == time){
			return true;
		}
		this.needUpdate = false;
		this.lastUpdate = time;
		//根据时间,变速情况和缩放比, 计算y应该在哪里

		this.noteAreaY = this.timeToLayer(time);
		this.line.css('webkitTransform','translate3d(0,'+(this.noteAreaY - this._gridBtY)+'px,0)');
		this.note.css('webkitTransform','translate3d(0,'+(this.noteAreaY - this._gridBtY)+'px,0)');
	},


	selectAll: function () {
		this.note.children().addClass('curr');
		this.pluginMeta.update();
	},

	cancelSelect: function(){
		this.note.find('.curr').removeClass('curr');
		this.pluginCopy.onCancel();
		this.pluginMeta.clear();
		this.stopPlay();
	},

	deleteSelect: function () {
        this.pluginUndo.mark("before_del");
		var notes = this.note.find('.curr');
		var arr = this.getSelectIds();
		notes.remove();
		this.manager.removeNotes(arr);
		this.cancelSelect();
	},

	getSelectIds: function(){
		var notes = this.note.find('.curr');
		var arr = [];
		for(var i=notes.size()-1;i>=0;i--){
			var id = +notes.eq(i).data('tag');
			arr.push(id);
		}

		return arr;
	},

	getSelectCount: function(){
		return this.note.find('.curr').size();
	},

	showLine: function(beat){
		if(!beat && this.currline){
			this.currline.remove();
			this.currline = null;
		}else if(beat){
			var y = this.gridToLayerY(beat);
			if(!this.currline){
				this.currline = $('<i class="red"></i>');
				this.line.append(this.currline);
			}
			this.currline.css('bottom', y);
			//seek到红线附近
			var baseline;
			if(this.gridScale < 1.5){
				baseline = frac.subtract(beat, [1, 0, 2]);
			}else{
				baseline = frac.subtract(beat, [0, 1, 2]);
			}
			if(baseline[0] < 0){
				baseline = [0, 0, 1];
			}
			var time = timing.beatToTime(baseline);
			sound.seekTo(time);  //seek里有范围限定
		}
	},

	/**
	 * 辅助方法
	 */

	alignNote: function(note, div){
		var y = this.gridToLayerY(note.beat);
		var conf = {
			bottom: y
		};
		conf.left = note.column * this._columnW;
		if(note.endbeat){
			var _y = this.gridToLayerY(note.endbeat);
			conf.height = Math.abs(_y - y);
		}else{
			conf.height = this._noteH;
		}
		div.css(conf);
	},

	makeNoteDiv: function(note){
		var y = this.gridToLayerY(note.beat);
		var x = note.column * this._columnW;
		var h = 20;
		if(note.endbeat){
			var _y = this.gridToLayerY(note.endbeat);
			h = Math.abs(_y - y);
		}
		var style = 'height:'+h+'px;left:'+x+'px;bottom:'+y+'px';
        var cls = "";
        if(note.isBgm){
            //style += ";display:none";
            cls = ' class="bgm-note" ';
        }
		if(typeof note.color == 'number'){
			var _color = layer.getColor(note.color);
			if(_color){
				style += ';background-color:'+ _color;
			}
		}
		return '<div ' + cls + ' style="'+style+'" data-tag="'+note.id+'"></div>';
	},

	/**
	 * 根据note 逻辑数据定位到图形div
	 */
	getNoteDiv: function(id){
		return this.note.find('div[data-tag="'+id+'"]');  //这个如果不存在, 就一定是数据同步出问题了
	},

	getDivOffset: function (div) {
		return {
			bottom: parseFloat(div.css('bottom')) || 0,
			left: parseFloat(div.css('left')) || 0
		}
	},

	playNote: function(note){
		if(!note.sound){
			return;
		}

		if(note.id == this.playNoteId){
			this.stopPlay();
			this.playNoteId = -1;
		}
		var pid = sound.load(sample.getFilePath(note.sound));
		if(pid == -1){
			return;
		}
		this.playId = pid;
		this.playNoteId = note.id;
		sound.fmod.volumeAudio(note.vol || 30);
		sound.fmod.seekAudio(0);
		sound.fmod.playAudio();
	},

	stopPlay: function(){
		if(this.playId != -1){
			sound.fmod.selectAudio(this.playId);
			sound.fmod.stopAudio();
			this.playId = -1;
		}
	},

    getToggleRange : function(){
        var chart = this.manager.chart;
        if(!chart){
            return;
        }
        var toggle = chart.extra.toggle;
        if(!toggle || toggle.length == 0){
            return;
        }
        var min = 999999;
        var max = -1;
        for(var k in toggle){
            if(toggle[k] == 1){
                k = parseInt(k);
                if(k > max){
                    max = k;
                }
                if(k < min){
                    min = k;
                }

            }
        }
        return [min, max];
    },

    // 对note进行水平镜像
    mirrorFlip : function(){
        var range = this.getToggleRange();
        if(!range || range[0] == range[1]){
            return;
        }
        var notes = this.manager.getSelectNotes();
        for(var i = 0; i < notes.length; i++) {
            var mirNote = util.cloneObject(notes[i]);
            mirNote.column = range[0] + range[1]  - mirNote.column;
            this.manager.addNote(mirNote);
            var noteDiv = this.pluginEdit.addNoteDiv(mirNote);
            this.alignNote(mirNote, noteDiv);
        }
    },

	// 水平翻转note
	flipNotes : function(){
		var div = this.note.find('.curr');
		var note = [];
		var len = div.size();
		if(len == 0){
			return;
		}

		//最大最小column
		var min = 9999999;
		var max = -1;
		for(var i=0;i<len;i++){
			var id = +div.eq(i).data('tag');
			var _note = this.manager.getNote(id);
			note.push(_note);
			if(_note.column > max){
				max = _note.column;
			}
			if(_note.column < min){
				min = _note.column;
			}
		}
		if(max == min){
			return;
		}
		for(var i=0;i<len;i++){
			var _note = note[i];
			_note.column = max + min - _note.column;
			this.alignNote(_note, div.eq(i));
		}
		event.trigger(event.events.note.change);
	},

	/**
	 * 获得栅格最下面一格的beat值, 可能有一格的偏差
	 */
	getGridBottomBeat: function () {
		var time = sound.stableTime();
		var beat = timing.timeToBeat(time, this.divide);
		//不验合法性?
		return beat;
	},

	/**
	 * 鼠标坐标到画布坐标(左下角为原点)
	 * @param param  坐标数组
	 * @returns {*[]}
	 */
	mouseToLayer: function(param){
		var h = this.gridH;
		h = h - param[1];
		return [param[0], h + this.noteAreaY - this._gridBtY];
	},

	/**
	 * 画布坐标映射到栅格坐标，到最近的栅格
	 * @param param
	 */
	layerToGrid: function(param){
		var beat = frac.fromFloat(param[1] / this.gridScaleH, this.divide);
		var x = Math.floor((param[0] + 2) / this._columnW);
		if(x < 0){
			beat.push(0);
			beat.push(-1);
		}else if(x > this._columnCount - 1){
			beat.push(this._columnCount - 1);
			beat.push(-1);
		}else{
			beat.push(x);
		}
		return beat;
	},

	/**
	 * 画布坐标映射到栅格坐标，假设栅格上摆满note时到最近note中心点
	 * @param param
	 */
	layerToGridOffset: function(param){
		var beat = frac.fromFloat(param[1] / this.gridScaleH - this._noteH * 0.5 / this._beatH, this.divide);
		var x = Math.floor((param[0] + 2) / this._columnW);
		if(x < 0){
			beat.push(0);
			beat.push(-1);
		}else if(x > this._columnCount - 1){
			beat.push(this._columnCount - 1);
			beat.push(-1);
		}else{
			beat.push(x);
		}
		return beat;
	},

	/**
	 * 画布坐标映射到栅格坐标，y轴不对齐（实际按1/288对齐）
	 * @param param
	 */
	layerToGridUnsnap: function(param){
		var beat = frac.fromFloat(param[1] / this.gridScaleH, 288);
		var x = Math.floor((param[0] + 2) / this._columnW);
		if(x < 0){
			beat.push(0);
			beat.push(-1);
		}else if(x > this._columnCount - 1){
			beat.push(this._columnCount - 1);
			beat.push(-1);
		}else{
			beat.push(x);
		}
		return beat;
	},

	mouseToGrid: function(param){
		var pos = this.mouseToLayer(param);
		return this.layerToGrid(pos);
	},

	mouseToGridOffset: function(param){
		var pos = this.mouseToLayer(param);
		return this.layerToGridOffset(pos);
	},

	mouseToGridUnsnap: function(param){
		var pos = this.mouseToLayer(param);
		return this.layerToGridUnsnap(pos);
	},

	/**
	 * beat值映射到画布y值, 对齐
	 * @param param
	 * @returns {number}
	 */
	gridToLayerY: function(param){
		return this.gridScaleH * frac.toFloat(param);
	},

	/**
	 * 画布坐标到对应的时间(对齐到栅格)
	 * @param y y偏移值
	 */
	layerToTime: function(y){
		return timing.floatBeatToTime(y / this.gridScaleH);
	},

	/**音频时间到画布坐标*/
	timeToLayer: function(time){
		return this.gridScaleH * timing.timeToFloatBeat(time);
	},

	/**
	 * note对象转为4值数组
	 */
	noteToParam: function(obj){
		return [obj.beat[0], obj.beat[1], obj.beat[2], obj.column];
	},

	paramToNote: function(param){
		return {
			beat: [param[0], param[1], param[2]],
			column: param[3],
			tid: -1
		};
	}
};

module.exports = GridUI;